package com.biboheart.adapter.support.utils;

import com.biboheart.adapter.compiler.ast.Node;
import com.biboheart.adapter.compiler.enums.NodeType;
import com.biboheart.adapter.compiler.token.Token;
import com.biboheart.adapter.support.enums.LogicalEnum;
import com.biboheart.adapter.support.enums.OperatorEnum;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class ObjectUtils {
    @SuppressWarnings("unchecked")
    public static Object getValue(Object obj, String name) {
        if (null == obj || null == name || name.isEmpty()) {
            return null;
        }
        if (obj instanceof Map) {
            Map<String, Object> map = ObjectTypeUtils.convertType(obj, Map.class);
            return map.get(name);
        } else {
            Class<?> clazz = obj.getClass();
            try {
                /*PropertyDescriptor pd = new PropertyDescriptor(name, clazz);
                Method getMethod = pd.getReadMethod();//获得get方法
                getMethod.setAccessible(true);
                return getMethod.invoke(obj);*/
                Field field = clazz.getDeclaredField(name);
                field.setAccessible(true);
                return field.get(obj);
            } catch (IllegalAccessException | NoSuchFieldException e) {
                return null;
            }
        }
    }

    @SuppressWarnings("unchecked")
    public static Object getValueByPath(Object obj, String namePath) {
        if (null == obj || null == namePath || namePath.isEmpty()) {
            return null;
        }
        String pattern = "\\.|\\[\\d+]";
        if (!namePath.matches(pattern)) {
            return getValue(obj, namePath);
        }
        String name = namePath.contains(".") ? namePath.substring(0, namePath.indexOf(".")) : namePath;
        int index = 0;
        String key = name;
        if (key.contains("[")) {
            key = name.substring(0, name.indexOf("["));
            index = Integer.parseInt(name.substring(name.indexOf("[") + 1, name.indexOf("]")));
        }
        Object next = getValue(obj, key);
        if (null == next) {
            return null;
        }
        if (next instanceof List) {
            next = ((List<Object>)next).get(index);
        }
        return getValueByPath(next, namePath.substring(name.length()));
    }

    // 通过ast从对象中取值
    public static Object calcValue(Node ast, Object sourceObject) {
        if (null == ast) {
            return null;
        }
        if (NodeType.NODE_ROOT.equals(ast.getNodeType())) {
            ast = ast.getChildren().get(0);
        }
        NodeType nodeType = ast.getNodeType();
        Token token = ast.getToken();
        if (NodeType.NODE_CONSTANT.equals(nodeType)) {
            return token.getVal();
        }
        if (NodeType.NODE_VARIABLE.equals(nodeType)) {
            if ("value".equals(token.getVal()) && ObjectTypeUtils.isPrimitive(sourceObject)) {
                return sourceObject;
            }
            return ObjectUtils.getValue(sourceObject, String.valueOf(token.getVal()));
        }
        List<Node> children = ast.getChildren();
        if (null == children || children.isEmpty()) {
            return null;
        }
        List<Object> objectList = new ArrayList<>();
        for (Node child : children) {
            objectList.add(calcValue(child, sourceObject));
        }
        Object value = null;
        if (NodeType.NODE_COMMAND.equals(nodeType)) {
            String instructName = token.getCode();
            Object input = objectList.get(0);
            Object[] arr;
            if (objectList.size() > 1) {
                arr = new Object[objectList.size() - 1];
                for (int i = 1; i < objectList.size(); i ++) {
                    arr[i - 1] = objectList.get(i);
                }
            } else {
                arr = new Object[0];
            }
            value = ActuatorUtils.execute(instructName, input, arr);
        } else if (NodeType.NODE_OPERATOR.equals(nodeType)) {
            Object left = objectList.get(0);
            Object right = objectList.size() > 1 ? objectList.get(1) : null;
            if (LogicalEnum.OPERATOR_LOGICAL_NOT.code().equals(token.getCode())) {
                value = LogicalUtils.execute(LogicalEnum.OPERATOR_LOGICAL_NOT.code(), ObjectTypeUtils.convertType(left, Boolean.class));
            } else if (OperatorEnum.OPERATOR_BIT_INV.code().equals(token.getCode())) {
                value = CalculateUtils.execute(OperatorEnum.OPERATOR_BIT_INV.code(), ObjectTypeUtils.convertType(left, Number.class));
            } else if (LogicalEnum.contains(token.getCode())) {
                value = LogicalUtils.execute(token.getCode(), left, right);
            } else if (OperatorEnum.contains(token.getCode())) {
                value = CalculateUtils.execute(token.getCode(), ObjectTypeUtils.convertType(left, Number.class), ObjectTypeUtils.convertType(right, Number.class));
            }
        }
        return value;
    }
}
